package top.jfunc.common.servlet;

import top.jfunc.common.http.base.Method;
import top.jfunc.common.utils.IoUtil;
import top.jfunc.common.utils.MultiValueMap;
import top.jfunc.common.utils.ParamUtil;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Supplier;

/**
 * 提供body多次读取的功能，因为在参数签名中需要读取body
 */
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
	private String body;
	private final boolean supportMultiValue;
	private final Charset charset = StandardCharsets.UTF_8;

	public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
		this(request, false);
	}
	public MultiReadHttpServletRequest(HttpServletRequest request, boolean supportMultiValue) throws IOException {
		super(request);
		this.body = new String(IoUtil.stream2Bytes(request.getInputStream()));
		this.supportMultiValue = supportMultiValue;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	@Override
	public ServletInputStream getInputStream() {
		byte[] bodyBytes = body.getBytes(charset);
		final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bodyBytes);
		return new ServletInputStream() {
			@Override
			public int read() {
				return byteArrayInputStream.read();
			}

			@Override
			public int read(byte[] b) throws IOException {
				return byteArrayInputStream.read(b);
			}

			@Override
			public int read(byte[] b, int off, int len) throws IOException {
				return byteArrayInputStream.read(b, off, len);
			}

			@Override
			public int available() throws IOException {
				return bodyBytes.length;
			}

/*            @Override
            public boolean isFinished() {
                return false;
            }
 
            @Override
            public boolean isReady() {
                return false;
            }
 
            @Override
            public void setReadListener(ReadListener listener) {
 
            }*/
		};
	}

	@Override
	public String getParameter(String name) {
		return doIt(()-> getParameter(name, supportMultiValue),
		()->super.getParameter(name));
	}

	private String getParameter(String name , boolean supportMultiValue) {
		if(supportMultiValue){
			return ParamUtil.parseMultiValueParam(body).getFirst(name);
		}
		return ParamUtil.parseParam(body).get(name);
	}

	@Override
	public Map getParameterMap() {
		return doIt(()-> getParameterMap(supportMultiValue),
				super::getParameterMap);

	}

	private Map<String, String[]> getParameterMap(boolean supportMultiValue) {
		if(supportMultiValue){
			MultiValueMap<String, String> multiValueMap = ParamUtil.parseMultiValueParam(body);
			Map<String,String[]> hashMap = new HashMap<>(multiValueMap.size());
			multiValueMap.forEach((k,v)-> hashMap.put(k, v.toArray(new String[0])));
			return hashMap;
		}
		Map<String, String> map = ParamUtil.parseParam(body);
		Map<String,String[]> hashMap = new HashMap<>(map.size());
		map.forEach((k,v)-> hashMap.put(k,new String[]{v}));
		return hashMap;
	}

	@Override
	public Enumeration getParameterNames() {
		return doIt(()->{
			Map<String, String> map = ParamUtil.parseParam(body);
			return Collections.enumeration(map.keySet());
		}, super::getParameterNames);
	}

	@Override
	public String[] getParameterValues(String name) {
		return doIt(()-> getParameterValues(name,supportMultiValue),()->super.getParameterValues(name));
	}

	private String[] getParameterValues(String name, boolean supportMultiValue) {
		if(supportMultiValue){
			MultiValueMap<String, String> multiValueMap = ParamUtil.parseMultiValueParam(body);
			List<String> strings = multiValueMap.get(name);
			if(null == strings){
				return null;
			}
			return strings.toArray(new String[0]);
		}
		Map<String, String> map = ParamUtil.parseParam(body);
		return new String[]{map.get(name)};
	}

	protected <V> V doIt(Supplier<V> supplier, Supplier<V> supperSupplier){
		String method = getMethod();
		//只有post才需要
		if(Method.POST.name().equals(method)){
			return supplier.get();
		}else {
			return supperSupplier.get();
		}
	}

	@Override
	public BufferedReader getReader() {
		return new BufferedReader(new InputStreamReader(this.getInputStream()));
	}
}